#include <UnitTest++.h>
#include <HenkoCppLib.h>

#include "tools.h"

using namespace HenkoCppLib;

SUITE(Pointer)
{
	TEST(NullPointer)
	{
		StartMemoryLeakTest();
		{
			IntPtr p;
			CHECK(0 == p.GetNativePointer());
		}
		CheckMemoryLeaks();
	}

	TEST(MemoryAllocation)
	{
		StartMemoryLeakTest();
		{
			IntPtr p1 = new(p1) int;
			IntPtr p2 = new(p2) int[10];
			CHECK_EQUAL(1, p1.GetSize());
			CHECK_EQUAL(10, p2.GetSize());
			CHECK_EQUAL(4, p1.GetNumBytes());
			CHECK_EQUAL(40, p2.GetNumBytes());
		}
		CheckMemoryLeaks();
	}

	TEST(AssignValueConstructor)
	{
		StartMemoryLeakTest();
		{
			IntPtr p1 = new(p1) int[10];
			IntPtr p2 = p1;
			CHECK_EQUAL(2, p1.GetRefCount());
			CHECK_EQUAL(p1.GetNativePointer(), p2.GetNativePointer());
		}
		CheckMemoryLeaks();
	}

	TEST(AssignValueOperator)
	{
		StartMemoryLeakTest();
		{
			IntPtr p1;
			IntPtr p2;
			p1 = new(p1) int[10];
			p2 = p1;
			CHECK_EQUAL(2, p1.GetRefCount());
			CHECK_EQUAL(p1.GetNativePointer(), p2.GetNativePointer());
		}
		CheckMemoryLeaks();
	}

	TEST(ReferenceCounter)
	{
		StartMemoryLeakTest();
		{
			IntPtr p1 = new(p1) int[10];
			IntPtr p2 = p1;
			IntPtr p3 = p1;
			IntPtr p4 = p1;
			IntPtr p5 = p1;
			CHECK_EQUAL(5, p1.GetRefCount());
		}
		CheckMemoryLeaks();
	}

	TEST(2DimArray)
	{
		StartMemoryLeakTest();
		{
			IntPtr2D p = new(p) IntPtr[100];
			for (p = 0; p; ++p)
			{
				*p = new(*p) int[100];
			}
		}
		CheckMemoryLeaks();
	}

	TEST(3DimArray)
	{
		StartMemoryLeakTest();
		{
			IntPtr3D p = new(p) IntPtr2D[100];
			for (p = 0; p; ++p)
			{
				*p = new(*p) IntPtr[100];
				for (*p=0; *p; ++*p)
				{
					**p = new(**p) int[100];
				}
			}
		}
		CheckMemoryLeaks();
	}

	IntPtr CreateArray(int length)
	{
		IntPtr result = new(result) int[length];
		return result;
	}

	TEST(FunctionReturn)
	{
		StartMemoryLeakTest();
		{
			IntPtr p;
			for (int i = 1; i <= 10; i++)
			{
				p = CreateArray(i);
			}
		}
		CheckMemoryLeaks();
	}

	TEST(ArrayOperatorChar)
	{
		StartMemoryLeakTest();
		{
			CharPtr str = new(str) char[100];
			str.MemCopyFromNative("This is a test!", 16);

			CHECK_EQUAL('T', str[0]);
			CHECK_EQUAL('h', str[1]);
			CHECK_EQUAL('i', str[2]);
			CHECK_EQUAL('s', str[3]);

			str[14] = '.';
			CHECK_EQUAL('.', str[14]);

			CHECK_EQUAL("This is a test.", str.GetNativePointer());

			str[15] = ' ';
			str[16] = 'S';
			str[17] = 'u';
			str[18] = 'c';
			str[19] = 'c';
			str[20] = 'e';
			str[21] = 's';
			str[22] = 's';
			str[23] = '!';
			str[24] = '\0';

			CHECK_EQUAL("This is a test. Success!", str.GetNativePointer());
		}
		CheckMemoryLeaks();
	}

	TEST(ArrayOperatorInt)
	{
		StartMemoryLeakTest();
		{
			IntPtr p = new(p) int[5];
			p[0] = 10000;
			p[1] = 20000;
			p[2] = 30000;
			p[3] = 40000;
			p[4] = 50000;

			CHECK_EQUAL(10000, p[0]);
			CHECK_EQUAL(20000, p[1]);
			CHECK_EQUAL(30000, p[2]);
			CHECK_EQUAL(40000, p[3]);
			CHECK_EQUAL(50000, p[4]);
		}
		CheckMemoryLeaks();
	}

	TEST(IndirectionOperator)
	{
		StartMemoryLeakTest();
		{
			CharPtr str = new(str) char[100];
			str.MemCopyFromNative("This is a test!", 16);

			CHECK_EQUAL('T', *str);

			IntPtr p = new(p) int[10];
			p[0] = 10000;

			CHECK_EQUAL(10000, *p);
		}
		CheckMemoryLeaks();
	}

	TEST(PointerArithmeticsChar)
	{
		StartMemoryLeakTest();
		{
			CharPtr str = new(str) char[100];
			str.MemCopyFromNative("This is a test!", 16);

			CHECK_EQUAL('T', *str);
			CHECK_EQUAL('h', *(str+1));
			CHECK_EQUAL('i', *(str+2));
			CHECK_EQUAL('s', *(str+3));

			CHECK_EQUAL("a test!", (str + 8).GetNativePointer());
			CHECK_EQUAL("is a test!", ((str + 8)-3).GetNativePointer());

			str += 5;
			CHECK_EQUAL("is a test!", str.GetNativePointer());

			str += 3;
			CHECK_EQUAL("a test!", str.GetNativePointer());

			str += 2;
			CHECK_EQUAL("test!", str.GetNativePointer());

			str -= 2;
			CHECK_EQUAL("a test!", str.GetNativePointer());

			str += 7;
			CHECK_EQUAL("", str.GetNativePointer());

			str -= 7;
			CHECK_EQUAL("a test!", str.GetNativePointer());
			CHECK_EQUAL("test!", (str+2).GetNativePointer());
			CHECK_EQUAL("is a test!", (str-3).GetNativePointer());

			str.ResetOffset();
			CHECK_EQUAL("This is a test!", str.GetNativePointer());

			str = 5;
			CHECK_EQUAL("is a test!", str.GetNativePointer());
			CHECK_EQUAL("a test!", (str+3).GetNativePointer());
		}
		CheckMemoryLeaks();
	}

	TEST(PointerArithmeticsInt)
	{
		StartMemoryLeakTest();
		{
			IntPtr p = new(p) int[5];
			p[0] = 10000;
			p[1] = 20000;
			p[2] = 30000;
			p[3] = 40000;
			p[4] = 50000;

			CHECK_EQUAL(true, p.IsAtFirst());

			CHECK_EQUAL(10000, *p);
			CHECK_EQUAL(20000, *(p+1));
			CHECK_EQUAL(30000, *(p+2));
			CHECK_EQUAL(40000, *(p+3));
			CHECK_EQUAL(50000, *(p+4));

			p++;
			CHECK_EQUAL(20000, *p);

			p+=2;
			CHECK_EQUAL(40000, *p);
			CHECK_EQUAL(50000, *(p+1));
			CHECK_EQUAL(30000, *(p-1));

			CHECK_EQUAL(true, p.IsBeforeLast());
			CHECK_EQUAL(false, p.IsAtLast());

			p++;

			CHECK_EQUAL(false, p.IsBeforeLast());
			CHECK_EQUAL(true, p.IsAtLast());

			p-=4;

			CHECK_EQUAL(true, p.IsAtFirst());
		}
		CheckMemoryLeaks();
	}

	TEST(PointerConversion)
	{
		StartMemoryLeakTest();
		{
			UIntPtr p1 = new(p1) unsigned int[3];
			UCharPtr p2 = p1;

			CHECK_EQUAL((void*)p1.GetNativePointer(), (void*)p2.GetNativePointer());

			CHECK_EQUAL(3, p1.GetSize());
			CHECK_EQUAL(3, p2.GetSize());

			p1[0] = 0x11223344;
			p1[1] = 0x55667788;
			p1[2] = 0x99AABBCC;

			CHECK_EQUAL(0x44, p2[0]);
			CHECK_EQUAL(0x88, p2[1]);
			CHECK_EQUAL(0xCC, p2[2]);
		}
		CheckMemoryLeaks();
	}


	class SampleBase
	{
	protected:
		int a;
		int b;

	public:
		SampleBase()
		{
			a = 0;
			b = 0;
		}

		SampleBase(int a, int b)
			:a(a), b(b)
		{
		}

		int GetA()
		{
			return a;
		}

		int GetB()
		{
			return b;
		}

		void SetAB(int a, int b)
		{
			this->a = a;
			this->b = b;
		}
	};

	TEST(CustomClassConstruct)
	{
		StartMemoryLeakTest();
		{
			Pointer<SampleBase> p1 = new(p1) SampleBase(3, 4);
			CHECK_EQUAL(3, p1->GetA());
			CHECK_EQUAL(4, p1->GetB());

			Pointer<SampleBase> p2 = new(p2) SampleBase[3];
			p2[0].SetAB(5, 6);
			p2[1].SetAB(7, 8);
			p2[2].SetAB(9, 10);

			CHECK_EQUAL(5, p2->GetA());
			CHECK_EQUAL(6, p2->GetB());
			p2++;
			CHECK_EQUAL(7, p2->GetA());
			CHECK_EQUAL(8, p2->GetB());
			p2++;
			CHECK_EQUAL(9, p2->GetA());
			CHECK_EQUAL(10, p2->GetB());
		}
		CheckMemoryLeaks();
	}

	class SampleDerived : public SampleBase
	{
		int c;
		int d;

	public:
		SampleDerived()
			: SampleBase(20, 30)
		{
			c = 0;
			d = 0;
		}

		SampleDerived(int c, int d)
			: SampleBase(c*10, d*10), c(c), d(d)
		{
		}

		int GetC()
		{
			return c;
		}

		int GetD()
		{
			return d;
		}

		void SetCD(int c, int d)
		{
			this->a = c*10;
			this->b = d*10;
			this->c = c;
			this->d = d;
		}
	};

	TEST(CustomDerivedClass)
	{
		StartMemoryLeakTest();
		{
			Pointer<SampleDerived> p1 = new(p1) SampleDerived(2, 3);

			Pointer<SampleBase> p2 = p1;
			CHECK_EQUAL(20, p2->GetA());
			CHECK_EQUAL(30, p2->GetB());

			Pointer<SampleDerived> p3 = p2;
			CHECK_EQUAL(2, p3->GetC());
			CHECK_EQUAL(3, p3->GetD());
		}
		CheckMemoryLeaks();
	}

	TEST(CustomDerivedClassArray)
	{
		StartMemoryLeakTest();
		{
			Pointer<SampleDerived> p1 = new(p1) SampleDerived[3];

			p1[0].SetCD(1,2);
			p1[1].SetCD(3,4);
			p1[2].SetCD(5,6);

			Pointer<SampleBase> p2 = p1;
			CHECK_EQUAL(10, p2[0].GetA());
			CHECK_EQUAL(20, p2[0].GetB());
			CHECK_EQUAL(30, p2[1].GetA());
			CHECK_EQUAL(40, p2[1].GetB());
			CHECK_EQUAL(50, p2[2].GetA());
			CHECK_EQUAL(60, p2[2].GetB());

			Pointer<SampleDerived> p3 = p2;
			CHECK_EQUAL(1, p3[0].GetC());
			CHECK_EQUAL(2, p3[0].GetD());
			CHECK_EQUAL(3, p3[1].GetC());
			CHECK_EQUAL(4, p3[1].GetD());
			CHECK_EQUAL(5, p3[2].GetC());
			CHECK_EQUAL(6, p3[2].GetD());
		}
		CheckMemoryLeaks();
	}

}
